{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用@property\n",
    "\n",
    "Python内置的@property装饰器就是<font color=red>负责把一个方法变成属性调用的</font>.使得有既能检查参数，又可以用类似属性这样简单的方式来访问类的变量。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "把一个`getter`方法变成属性，只需要加上`@property`就可以了，此时，`@property`本身又创建了另一个装饰器`@score.setter`，负责把一个`setter`方法变成属性赋值，于是，我们就拥有一个可控的属性操作."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "s.score:  60\n",
      "s.name:  Zach\n"
     ]
    },
    {
     "ename": "ValueError",
     "evalue": "score must between 0 ~ 100",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-1-76ceeb8aa98f>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m     22\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"s.score: \"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mscore\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     23\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"s.name: \"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mname\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 24\u001b[1;33m \u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mscore\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m9999\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     25\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"s.score: \"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mscore\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m<ipython-input-1-76ceeb8aa98f>\u001b[0m in \u001b[0;36mscore\u001b[1;34m(self, value)\u001b[0m\n\u001b[0;32m     10\u001b[0m             \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"score must be an integer\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     11\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[0mvalue\u001b[0m \u001b[1;33m<\u001b[0m \u001b[1;36m0\u001b[0m \u001b[1;32mor\u001b[0m \u001b[0mvalue\u001b[0m \u001b[1;33m>\u001b[0m \u001b[1;36m100\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 12\u001b[1;33m             \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"score must between 0 ~ 100\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     13\u001b[0m         \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__score\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     14\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mValueError\u001b[0m: score must between 0 ~ 100"
     ]
    }
   ],
   "source": [
    "class Student(object):\n",
    "    \n",
    "    @property\n",
    "    def score(self): # 方法score()变成了属性score\n",
    "        return self.__score\n",
    "    \n",
    "    @score.setter\n",
    "    def score(self, value): # 方法score()变成了属性score\n",
    "        if not isinstance(value, int):\n",
    "            raise ValueError(\"score must be an integer\")\n",
    "        if value < 0 or value > 100:\n",
    "            raise ValueError(\"score must between 0 ~ 100\")\n",
    "        self.__score = value\n",
    "        \n",
    "        \n",
    "    @property\n",
    "    def name(self): # 方法name()变成了属性name\n",
    "        return \"Zach\"\n",
    "    \n",
    "s = Student()\n",
    "s.score = 60 # 设置属性\n",
    "print(\"s.score: \", s.score)\n",
    "print(\"s.name: \", s.name)\n",
    "s.score = 9999\n",
    "print(\"s.score: \", s.score)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Rectangle(object):\n",
    "    def __init__(self):\n",
    "        self.width = 0\n",
    "        self.height = 0\n",
    "        \n",
    "    def set_size(self, size):\n",
    "        self.width, self.height = size\n",
    "        \n",
    "    def get_size(self):\n",
    "        return self.width, self.height\n",
    "    \n",
    "    # 把方法get_size()和set_size()变为属性的另一种方式\n",
    "    # 下述表达式的顺序不能变\n",
    "    # 调用的时候,调用size\n",
    "    size = property(get_size, set_size)\n",
    "    \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过调用函数`property`并将存取方法作为参数<font color=green>（ 获取方法在前，设置方法在后）</font>创建了一个特性，然后将名称`size`关联到这个特性。这样，你就能以同样的方式对待`width`、 `height`和`size`，而无需关心它们是如何实现的."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "r.size:  (10, 5)\n",
      "r.width:  150\n"
     ]
    }
   ],
   "source": [
    "r = Rectangle()\n",
    "r.width = 10\n",
    "r.height = 5\n",
    "print(\"r.size: \", r.size)\n",
    "r.size = 150, 100\n",
    "print(\"r.width: \", r.width)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=green>`property`其实并不是函数，而是一个类</font>。\n",
    "\n",
    "它的实例包含一些魔法方法，而所有的魔法都是由这些方法完成的。这些魔法方法为`__get__`、 `__set__`和`__delete__`，它们一道定义了所谓的描述符协议。只要对象实现了这些方法中的任何一个，它就是一个描述符。描述符的独特之处在于其访问方式。例如，读取属性（具体来说，是在实例中访问类中定义的属性）时，如果它关联的是一个实现了`__get__`的对象，将不会返回这个对象，而是调用方法`__get__`并将其结果返回。实际上，这是隐藏在特性、关联的方法、静态方法和类方法以及super后面的机制。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
